EF object relational mapping met code
- We moeten referenties in ons domeinmodel toevoegen naamruimten en assemblies waarin de attributen worden gedefinieerd. Deze vervuiling heet 'domain vervuiling'.
- We kunnen dingen niet dynamisch veranderen; attributen worden statisch en kunnen niet worden overschreven.
- Er is geen centrale locatie waar we onze eigen conventies kunnen afdwingen.
Entity Framework Code First biedt een extra mapping API waarmee je die beperkingen kan overkomen: code mapping of fluent mapping. Alle functionaliteit van de attribuut-gebaseerde mapping is aanwezig en nog veel meer. We laten zien hoe je de meest voorkomende scenario's met code mapping kan implemeteren.
Fluent, of code, mapping wordt ingesteld op een instantie van de DbModelBuilder klasse. De plaats waar we kunnen toegang krijgen tot een DbModelBuilder instantie is in de OnModelCreating methode van de DbContext.
public class ProjectsContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//configuration goes here
base.OnModelCreating(modelBuilder);
}
}
Deze infrastructuur methode wordt aangeroepen door Entity Framework wanneer het een context initialiseert. En dat nadat het automatisch alle entiteit-klassen waarnaar verwezen wordt in die context als DbSet
Schema
Voorbeelden van entiteitsmapping met behulp van code:
//set the table and schema modelBuilder.Entity().ToTable("Curstomer", "dbo"); //ignoring an entity and all properties of its type modelBuilder.Ignore ();
Hierboven zie je een voorbeeld van mapping van individuele eigenschappen. Maar de API laat het aaneenschakelen (chaining) van meerdere calls toe. Hier zie je hoe je tegelijkertijd de naam kolom, type, de maximale lengte, en het required attribuut kan instellen. Dit maakt de code leesbaarder.
//ignore a property modelBuilder.Entity().Ignore(x => x.FullName); //set a property’s values (column name, type, length, nullability) modelBuilder.Entity ().Property(x => x.FirstName). HasColumnName("FirstName"). HasColumnType("NVARCHAR"). HasMaxLength(50).IsRequired();
Primaire sleutels
Primary keys kan je als volgt creëren en genereren:
//setting a property as the key
modelBuilder.Entity<Curstomer>().HasKey(x => x.ProjectId);
//and the primary key generation strategy
modelBuilder.Entity<Curstomer>().Property(x => x.CustomerId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
//composite keys
modelBuilder.Entity<Curstomer>().HasKey(x => new { x.FirstName, x.LastName, x.BirthDay });
Vreemde sleutels
Navigatie eigenschappen - de naam voor foreign keys in EFCF jargon - maak je als volgt:
//a bidirectional many-to-one and its inverse with cascade
modelBuilder.Entity<Project>().HasRequired(x => x.Customer).
WithMany(x => x.Projects).WillCascadeOnDelete(true);
//a bidirectional one-to-many
modelBuilder.Entity<Customer>().HasMany(x => x.Projects) .
WithRequired(x => x.Customer);
//a bidirectional many-to-many
modelBuilder.Entity<Technology>().HasMany(x => x.Resources) .
WithMany(x => x.Technologies);
//a bidirectional one-to-one-or-zero with cascade
modelBuilder.Entity<Project>().HasOptional(x => x.Detail) .
WithRequired(x => x.Project).WillCascadeOnDelete(true);
//a bidirectional one-to-one (both sides required) with cascade
modelBuilder.Entity<Project>().HasRequired(x => x.Detail) .
WithRequiredPrincipal(x => x.Project).WillCascadeOnDelete(true);
//a bidirectional one-to-many with a foreign key property (CustomerId)
modelBuilder.Entity<Project>().HasRequired(x => x.Customer).
WithMany(x => x.Projects) .HasForeignKey(x => x.CustomerId);
//a bidirectional one-to-many with a non-conventional foreign key column
modelBuilder.Entity<Project>().HasRequired(x => x.Customer).
WithMany(x => x.Projects) .Map(x => x.MapKey("FK_Customer_Id"));
Berekende kolommen
Een eenvoudige kolom die wordt gegenereerd in de database door een formule, in plaats van fysiek opgeslagen:
modelBuilder.Entity<Customer>().Property(x => x.FullName).
HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
Een eigen configuratie klasse
De OnModelCreating methode kan behoorlijk complex worden. Het EFCF biedt dan ook de mogelijkheid om configuraties te groeperen in eigen klasse. Deze klasse moet overerven van EntityTypeConfiguration <T>, Hier is een voorbeeld:
modelBuilder.Configurations.Add(new CustomerConfiguration());
public class CustomerConfiguration : EntityTypeConfiguration<Customer>
{
public CustomerConfiguration()
{
this.Table("FK_Customer_Id", "dbo");
this.Property(x => x.Name).HasMaxLength(50).IsRequired();
}
}